昨日提及到使用到 useState
時,當 state 有變動時,就會觸發元件重新渲染,更新畫面。
當有個情境是基於 state 原有的值去計算新值並連續用 setState
來更新時,我們會怎麼做呢?
來看下方的範例,點擊按鈕後會連續累加+1兩次。預計點擊按鈕後,會讓 count 的值從 0 更改為 2。
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
setCount(count + 1);
};
return (
<div>
<p>當前計數:{count}</p>
<button onClick={handleClick}>增加兩次Count</button>
</div>
);
}
export default Counter;
但實際按下去後,count的值並沒有變成 2 ,而是 1 ,為何會這樣呢?
主要的原因是 React 的 batch update 機制。
在 React 中,為了提升效能,React 會將多個狀態更新合併為一個批次處理,避免多次不必要的重新渲染。這通常發生在事件處理中,例如在同一個按鈕點擊事件中連續呼叫多次 setState
。
相關的原理過程,可參考:[Day 14] 以 functional updater 來呼叫 setState - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天 (ithome.com.tw)
那今天想要在同個按鈕連續呼叫多次 setState
可以如何做呢?
解決這種需求方式,就是改以 updater function 的形式來進行 setState
的呼叫。
Updater function 是一種用來更新 state
的方式,
允許我們傳入一個函式作為 setState
的參數。
這個函式會接收前一次的 state
值,並根據該值來更新狀態。
setCount(prevValue => prevValue + 1);
updater function 能夠保證每次更新時使用的都是最新的 state
,
而不是呼叫 setState
當下的 state
值。
這能夠避免競態條件(race condition)和批次更新(batching)引發的問題。
將上述案例更改為 updater function
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
// 使用 updater function,將前一個狀態值傳入
setCount((prevCount) => prevCount + 1);
setCount((prevCount) => prevCount + 1);
};
return (
<div>
<p>當前計數:{count}</p>
<button onClick={handleClick}>增加兩次</button>
</div>
);
}
export default Counter;
setCount((prevCount) => prevCount + 1)
時,count
值傳入 prevCount
,這樣就確保每次更新都是基於最新的狀態值。setCount
,每次更新都會基於前一次的結果,最後 count
會正確地增加兩次。多次連續更新狀態:
當需要在同一個事件中多次更新狀態時,例如像計數器的應用,
updater function 確保每次更新都是基於最新的狀態。
setCount((prevCount) => prevCount + 1);
setCount((prevCount) => prevCount + 1);
依賴前一個狀態的更新:
當你的狀態更新邏輯需要依賴於前一個狀態的值時,例如逐步累加或倒數計…
setItems((prevItems) => prevItems + 1);
避免競態條件:
當多個狀態更新幾乎同時發生時,容易產生競態條件。如果每次更新都依賴於一個靜態值,而不是動態計算的結果,那麼可能會導致更新錯誤。使用 updater function,可以有效避免這些競態條件。
Functional Updater 是 React 中用來更新狀態的一個工具,特別適合用在需要依賴於前一個狀態值的情境中使用。它不僅能夠避免批次更新和競態條件問題,還能確保狀態的每次更新都是基於最新的值。
本文將會同步更新到我的部落格